/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkGbEndUser.h"
#include "MkUtil/MkLog.h"
#include "MkEvent/MkTimer.h"
#include "MkUtil/MkMd5.h"
#include "MkRtp/MkRtpUdpServer.h"
#include "MkRtp/MkRtpTcpClient.h"
#include "MkUtil/MkUtil.h"
#include "MkSdp/MkSdp.h"

CMkGbEndUser::CMkGbEndUser(CMkEventPool* pEventPool, CMkTimer* pTimer)
    : CMkGb28181(pEventPool, pTimer)
{

}

Uint32 CMkGbEndUser::Start(const MkGbStartParam& Param)
{
    return CMkGb28181::Start(Param, [this](const CMkGbRequest* pGbReq, const MkGbResponseCallback& Cb) {
        return OnGbRequest(pGbReq, Cb);
    });
}

Uint32 CMkGbEndUser::SetRegisterServer(const MkString& ServerId, const MkString& ServerRealm, const MkString& ServerHost, Uint16 Port, BOOL bTcpSip, const MkString& UserName, const MkString& Password, Uint32 Expires, Uint32 HbInterval, Uint32 HbCount, const MkGbSvrConnectCallback& ConnectCb)
{
    m_RegServerId = ServerId;
    m_RegServerRealm = ServerRealm;
    m_RegServerUserName = UserName;
    m_RegServerPassword = Password;
    m_RegExpires = Expires;
    m_HbCount = HbCount;
    m_HbInterval = HbInterval;
    m_RegHost = ServerHost;
    m_RegPort = Port;
    m_RegTcpSip = bTcpSip;
    AddRouter(ServerId, ServerRealm, ServerHost, Port, bTcpSip);
    CMkGbDeviceAuthorRequest Req;
    Req.m_RouterId = ServerId;
    Req.m_DstId = ServerId;
    Req.m_Realm = ServerRealm;
    Req.m_Expires = Expires;
    m_ConnectCb = ConnectCb;
    return RegisterServer(Req, 0 == Expires);
}

Uint32 CMkGbEndUser::OnGbRequest(const CMkGbRequest* pGbReq, const MkGbResponseCallback& Cb)
{
    Uint32 ErrorCode = NoneError;
    switch (pGbReq->m_RequestType) {
    case MkGbRequestBye:
        ErrorCode = OnGbByeRequest(dynamic_cast<const CMkGbByeReqeust*>(pGbReq), Cb);
    default:
        break;
    }
    MkCheckErrorLog(ErrorCode, "gb request parse error\n");
    Cb(NoneError, nullptr);
    return NoneError;
}

Uint32 CMkGbEndUser::OnGbByeRequest(const CMkGbByeReqeust* pGbReq, const MkGbResponseCallback& Cb)
{
    if (!pGbReq) {
        return InvalidParams;
    }
    DeletePlay(pGbReq->m_SessionId);
    return NoneError;
}

Uint32 CMkGbEndUser::ByeRequest(const MkString &PlaySession, const MkGbResponseCallback &Cb)
{
    DeletePlay(PlaySession);
    return CMkGb28181::ByeRequest(PlaySession, Cb);
}

Uint32 CMkGbEndUser::DeletePlay(const MkString& SessionId)
{
    MkGbEndUserPlayParam Param;
    m_MediaSourceLock.WLock();
    MkMap<MkString, MkGbEndUserPlayParam>::iterator it = m_MapMediaSource.find(SessionId);
    if (it != m_MapMediaSource.end()) {
        Param = it->second;
        m_MapMediaSource.erase(it);
    }
    m_MediaSourceLock.WUnlock();
    if (Param.StoppedCb) {
        Param.StoppedCb(SessionId);
    }
    return NoneError;
}

Uint32 CMkGbEndUser::RegisterServer(const CMkGbDeviceAuthorRequest& Req, BOOL bFailEnd/* = FALSE*/)
{
    return RegisterRequest(Req, [this, Req, bFailEnd](Uint32 ErrorCode, const CMkGbResponse* pResponse) {
        const CMkGbDeviceAuthorResponse *pRes = dynamic_cast<const CMkGbDeviceAuthorResponse*>(pResponse);
        if (MkSipStatusRequestTimeout + SipResponseErrorBegin == ErrorCode && 0 == Req.m_Expires) {
            DeleteRouter(Req.m_RouterId);
        }
        if (MkSipStatusUnauthorized + SipResponseErrorBegin == ErrorCode && pRes) {
            MkString Realm = pRes->m_Realm.substr(1, pRes->m_Realm.size() - 2);
            MkString Nonce = pRes->m_Nonce.substr(1, pRes->m_Nonce.size() - 2);

            if ("MD5" == pRes->m_Algorithm) {
                MkString Ha1 = CMkMd5Encode::Encode(m_RegServerUserName + ":" + Realm + ":" + m_RegServerPassword);
                MkString Ha2 = CMkMd5Encode::Encode("REGISTER:" + pRes->m_Uri);
                MkString Ha3 = CMkMd5Encode::Encode(Ha1 + ":" + Nonce + ":" + Ha2);
                CMkGbDeviceAuthorRequest ReReq;
                ReReq.m_AuthorType = pRes->m_AuthType;
                ReReq.m_UserName = m_RegServerUserName;
                ReReq.m_Algorithm = pRes->m_Algorithm;
                ReReq.m_Expires = Req.m_Expires;
                ReReq.m_Realm = Realm;
                ReReq.m_Nonce = Nonce;
                ReReq.m_RouterId = m_RegServerId;
                ReReq.m_Response = Ha3;
                ReReq.m_Uri = pRes->m_Uri;
                ReReq.m_DstId = m_RegServerId;
                ReReq.m_RegisterId = pRes->m_RegisterId;
                RegisterServer(ReReq);
            } else {
                ErrorCode = InvalidParams;
            }
        } else if (NoneError == ErrorCode) {
            if (0 == Req.m_Expires) {
                DeleteRouter(Req.m_RouterId);
            } else {
                m_LastActiveTime = NowMkTime;
                KeepAliveServer();
                GetTimer()->AddTimer([this]() {
                    KeepAliveServer();
                }, m_HbInterval * 1000, m_KeepAliverTimer);
                m_ConnectCb(TRUE);
            }
        } else {
            if (!bFailEnd) {
                GetTimer()->AddDelayer([this, Req]() {
                    RegisterServer(Req);
                }, 5 * 1000, m_RegisterDelayer);
            }
        }
    });
}
Uint32 CMkGbEndUser::KeepAliveServer()
{
    CMkGbMessageRequest Req;
    Req.m_RouterId = m_RegServerId;
    Req.m_DstId = m_RegServerId;
    Req.m_DeviceId = GetSipId();
    Req.m_CmdType = "Keepalive";
    Req.m_MessageType = "Notify";
    return MessageRequest(Req, [this](Uint32 ErrorCode, const CMkGbResponse* pGbRes) {
        if (NoneError == ErrorCode) {
            m_LastActiveTime = NowMkTime;
            return;
        }
        if (NowMkTime.OffsetSec(m_LastActiveTime) > (int)(m_HbInterval*m_HbCount)) {
            GetTimer()->AddDelayer([this]() {
                CMkGbDeviceAuthorRequest Req;
                Req.m_RouterId = m_RegServerId;
                Req.m_DstId = m_RegServerId;
                Req.m_Realm = m_RegServerRealm;
                Req.m_Expires = m_RegExpires;
                RegisterServer(Req);
            }, 5 * 1000, m_RegisterDelayer);
            m_ConnectCb(FALSE);
        }
    });
}

Uint32 CMkGbEndUser::StartPlay(const MkString& SipUri, BOOL bTcpSip, Uint32 WndIndex, const MkRtpParam& OfferRtpParam, const MkStreamReslutCallback& ResultCb, const MkStreamStoppedCallback& StreamStoppedCb)
{
    CMkSipUri Uri;
    Uint32 ErrorCode = Uri.Parse(SipUri);
    MkCheckErrorLog(ErrorCode, "input sipuri:%s invalid\n", SipUri.c_str());
    AddRouter(Uri.GetUserName(), "", Uri.GetHost(), Uri.GetPort(), FALSE);
    MkString OfferSdp;
    MkString strSsrc = std::to_string(OfferRtpParam.Ssrc);
    CMkSdp Sdp;
    CMkSdpMedia SdpMedia;
    Sdp.SetOrigin(CMkOrigin(GetSipId(), "0", "0", "IN", "IP4", OfferRtpParam.Host));
    Sdp.SetConnection(CMkSdpConnection("IN", "IP4", OfferRtpParam.Host));
    SdpMedia.SetPort(OfferRtpParam.Port);
    SdpMedia.SetProtocol(OfferRtpParam.bTcp ? "TCP/RTP/AVP" : "RTP/AVP");

    Sdp.SetVersion("0");
    Sdp.SetSessionName("Play");
    SdpMedia.SetMediaType("video");
    CMkSdpFmt SdpFmt;
    SdpFmt.SetPayload(OfferRtpParam.Payload);
    SdpFmt.SetEncodingName("PS");
    SdpFmt.SetClockRate(MkDefaultClockRate);
    SdpMedia.AddFmt(SdpFmt);
    SdpMedia.AddAttr(CMkSdpAttribute("recvonly"));
    if (OfferRtpParam.bTcp) {
        SdpMedia.AddAttr(CMkSdpAttribute("setup", "active"));   //mast active
        SdpMedia.AddAttr(CMkSdpAttribute("connection", "new"));
    }

    SdpMedia.AddExtend("y", strSsrc);
    Sdp.AddMedia(SdpMedia);
    Sdp.ToString(OfferSdp);
    MkString Subject = Uri.GetUserName() + ":0," + GetSipId() + ":" + std::to_string(WndIndex);
    return InviteRequest(Uri.GetUserName(), Uri.GetUserName(), Subject, OfferSdp, [this, strSsrc, ResultCb, StreamStoppedCb](Uint32 ErrorCode, const CMkGbResponse* pGbRes) {
        const CMkGbInviteResponse* pRes = dynamic_cast<const CMkGbInviteResponse*>(pGbRes);
        MkRtpParam AnswerRtpParam;
        if (!pGbRes) {
            ResultCb(SystemError, "", MkRtpParam(), nullptr);
            return;
        }
        MkString PlaySession = pRes->m_SessionId;
        if (NoneError != ErrorCode) {
            ResultCb(ErrorCode, "", MkRtpParam(), nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        CMkSdp Sdp;
        ErrorCode = Sdp.ParseContent(pRes->m_AnwserSdp);
        if (NoneError != ErrorCode || Sdp.GetMedia().empty()) {
            ResultCb(SystemError, "", MkRtpParam(), nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }

        AnswerRtpParam.Host = Sdp.GetOrigin().GetAddr();
        if (!Sdp.GetConnection().GetAddr().empty()) {
            AnswerRtpParam.Host = Sdp.GetConnection().GetAddr();
        }
        CMkSdpMedia SdpMedia = *Sdp.GetMedia().begin();
        if (!SdpMedia.GetSdpConnection().GetAddr().empty()) {
            AnswerRtpParam.Host = SdpMedia.GetSdpConnection().GetAddr();
        }
        MkString RemoteSsrc;
        SdpMedia.GetExtendValue("y", RemoteSsrc);
        Uint32 Ssrc = CMkUtil::SafeAtoi(RemoteSsrc);
        if (0 == Ssrc) {
            Ssrc = std::stoi(strSsrc);
        }
        MkVector<CMkSdpAttribute>::const_iterator it = SdpMedia.GetAttributes().begin();
        for (; it != SdpMedia.GetAttributes().end(); it++) {
            if ("setup" == it->GetField()) {
                if ("passive" != it->GetValue()) {
                    ResultCb(SystemError, "", MkRtpParam(), nullptr);
                    pRes->m_EventResCb(MkMapString(), "");
                    return;
                }
                break;
            }
        }
        if (SdpMedia.GetFmts().empty()
            || "PS" != SdpMedia.GetFmts().begin()->second.GetEncodingName()) {
            ResultCb(SystemError, "", MkRtpParam(), nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        AnswerRtpParam.bTcp = "TCP/RTP/AVP" == SdpMedia.GetProtocol();
        AnswerRtpParam.Port = SdpMedia.GetPort();
        AnswerRtpParam.Ssrc = Ssrc;
        AnswerRtpParam.Payload = SdpMedia.GetFmts().begin()->first;

        MkGbEndUserPlayParam Param;
        Param.PlaySession = pRes->m_SessionId;
        Param.ResultCb = ResultCb;
        Param.StoppedCb = StreamStoppedCb;
        m_MediaSourceLock.WLock();
        m_MapMediaSource[PlaySession] = Param;
        m_MediaSourceLock.WUnlock();
        ResultCb(NoneError, PlaySession, AnswerRtpParam, pRes->m_EventResCb);
    });
}

Uint32 CMkGbEndUser::StartPlay(const MkString& ChannelId, const MkString& PlayType, const CMkTime& BeginTime, const CMkTime& EndTime, Uint32 WndIndex, const MkRtpParam& OfferRtpParam, const MkStreamReslutCallback& ResultCb, const MkStreamStoppedCallback& StreamStoppedCb)
{
    MkString OfferSdp;
    char pSsrc[Len16] = { 0 };
    snprintf(pSsrc, Len16, "%04d", CMkUtil::GetRandomNumber() % 10000);
    MkString strSsrc = std::to_string(OfferRtpParam.Ssrc);
    CMkSdp Sdp;
    CMkSdpMedia SdpMedia;
    Sdp.SetOrigin(CMkOrigin(GetSipId(), "0", "0", "IN", "IP4", OfferRtpParam.Host));
    Sdp.SetConnection(CMkSdpConnection("IN", "IP4", OfferRtpParam.Host));
    SdpMedia.SetPort(OfferRtpParam.Port);
    SdpMedia.SetProtocol(OfferRtpParam.bTcp ? "TCP/RTP/AVP" : "RTP/AVP");

    Sdp.SetVersion("0");
    Sdp.SetSessionName(PlayType);
    if (!BeginTime.IsEmpty()) {
        Sdp.SetTime(CMkSdpTime(BeginTime.GetTvSec(), EndTime.GetTvSec(), MkVectorString()));
    }
    SdpMedia.SetMediaType("video");
    CMkSdpFmt SdpFmt;
    SdpFmt.SetPayload(96);
    SdpFmt.SetEncodingName("PS");
    SdpFmt.SetClockRate(MkDefaultClockRate);
    SdpMedia.AddFmt(SdpFmt);
    SdpMedia.AddAttr(CMkSdpAttribute("recvonly"));
    if (OfferRtpParam.bTcp) {
        SdpMedia.AddAttr(CMkSdpAttribute("setup", "active"));   //mast active
        SdpMedia.AddAttr(CMkSdpAttribute("connection", "new"));
    }

    SdpMedia.AddExtend("y", strSsrc);
    Sdp.AddMedia(SdpMedia);
    Sdp.ToString(OfferSdp);
    MkString Subject = ChannelId;
    if ("Play" == PlayType) {
        Subject += ":0," + GetSipId() + ":" + std::to_string(WndIndex);
    } else {
        Subject += ":1" + std::to_string(CMkUtil::GetRandomNumber()) + "," + GetSipId() + ":" + std::to_string(WndIndex);
    }
    return InviteRequest(m_RegServerId, ChannelId, Subject, OfferSdp, [this, strSsrc, ResultCb, StreamStoppedCb](Uint32 ErrorCode, const CMkGbResponse* pGbRes) {
        const CMkGbInviteResponse* pRes = dynamic_cast<const CMkGbInviteResponse*>(pGbRes);
        MkRtpParam AnswerRtpParam;
        if (!pGbRes) {
            ResultCb(SystemError, "", AnswerRtpParam, nullptr);
            return;
        }
        MkString PlaySession = pRes->m_SessionId;
        if (NoneError != ErrorCode) {
            ResultCb(ErrorCode, "", AnswerRtpParam, nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        CMkSdp Sdp;
        ErrorCode = Sdp.ParseContent(pRes->m_AnwserSdp);
        if (NoneError != ErrorCode || Sdp.GetMedia().empty()) {
            ResultCb(SystemError, "", AnswerRtpParam, nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }

        MkString RemoteHost = Sdp.GetOrigin().GetAddr();
        if (!Sdp.GetConnection().GetAddr().empty()) {
            RemoteHost = Sdp.GetConnection().GetAddr();
        }
        CMkSdpMedia SdpMedia = *Sdp.GetMedia().begin();
        if (!SdpMedia.GetSdpConnection().GetAddr().empty()) {
            RemoteHost = SdpMedia.GetSdpConnection().GetAddr();
        }
        MkString RemoteSsrc;
        SdpMedia.GetExtendValue("y", RemoteSsrc);
        Uint32 Ssrc = CMkUtil::SafeAtoi(RemoteSsrc);
        if (0 == Ssrc) {
            Ssrc = std::stoi(strSsrc);
        }
        MkVector<CMkSdpAttribute>::const_iterator it = SdpMedia.GetAttributes().begin();
        for (; it != SdpMedia.GetAttributes().end(); it++) {
            if ("setup" == it->GetField()) {
                if ("passive" != it->GetValue()) {
                    ResultCb(SystemError, "", AnswerRtpParam, nullptr);
                    pRes->m_EventResCb(MkMapString(), "");
                    return;
                }
                break;
            }
        }
        if (SdpMedia.GetFmts().empty()
            || 96 != SdpMedia.GetFmts().begin()->first
            || "PS" != SdpMedia.GetFmts().begin()->second.GetEncodingName()) {
            ResultCb(SystemError, "", MkRtpParam(), nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        AnswerRtpParam.bTcp = "TCP/RTP/AVP" == SdpMedia.GetProtocol();
        AnswerRtpParam.Host = RemoteHost;
        AnswerRtpParam.Port = SdpMedia.GetPort();
        AnswerRtpParam.Ssrc = Ssrc;

        MkGbEndUserPlayParam Param;
        Param.PlaySession = pRes->m_SessionId;
        Param.ResultCb = ResultCb;
        Param.StoppedCb = StreamStoppedCb;
        m_MediaSourceLock.WLock();
        m_MapMediaSource[PlaySession] = Param;
        m_MediaSourceLock.WUnlock();
        ResultCb(NoneError, PlaySession, AnswerRtpParam, pRes->m_EventResCb);
    });
}

Uint32 CMkGbEndUser::StopPlay(const MkString& SessionId)
{
    ByeRequest(SessionId, nullptr);
    DeletePlay(SessionId);
    return NoneError;
}

Uint32 CMkGbEndUser::StartPublish(const MkString& SipUri, BOOL bTcpSip, const MkRtpParam& OfferRtpParam, const MkStreamReslutCallback& ResultCb, const MkStreamStoppedCallback& StreamStoppedCb)
{
    CMkSipUri Uri;
    Uint32 ErrorCode = Uri.Parse(SipUri);
    MkCheckErrorLog(ErrorCode, "input sipuri:%s invalid\n", SipUri.c_str());
    AddRouter(Uri.GetUserName(), "", Uri.GetHost(), Uri.GetPort(), bTcpSip);

    MkString OfferSdp;
    MkString strSsrc = std::to_string(OfferRtpParam.Ssrc);
    CMkSdp Sdp;
    CMkSdpMedia SdpMedia;
    Sdp.SetOrigin(CMkOrigin(GetSipId(), "0", "0", "IN", "IP4", OfferRtpParam.Host));
    Sdp.SetConnection(CMkSdpConnection("IN", "IP4", OfferRtpParam.Host));
    SdpMedia.SetPort(OfferRtpParam.Port);
    SdpMedia.SetProtocol(OfferRtpParam.bTcp ? "TCP/RTP/AVP" : "RTP/AVP");

    Sdp.SetVersion("0");
    Sdp.SetSessionName("Play");
    SdpMedia.SetMediaType("video");
    CMkSdpFmt SdpFmt;
    SdpFmt.SetPayload(OfferRtpParam.Payload);
    SdpFmt.SetEncodingName("PS");
    SdpFmt.SetClockRate(MkDefaultClockRate);
    SdpMedia.AddFmt(SdpFmt);
    SdpMedia.AddAttr(CMkSdpAttribute("sendonly"));
    if (OfferRtpParam.bTcp) {
        SdpMedia.AddAttr(CMkSdpAttribute("setup", "active"));   //mast active
        SdpMedia.AddAttr(CMkSdpAttribute("connection", "new"));
    }

    SdpMedia.AddExtend("y", strSsrc);
    Sdp.AddMedia(SdpMedia);
    Sdp.ToString(OfferSdp);
    return InviteRequest(Uri.GetUserName(), Uri.GetUserName(), "", OfferSdp, [this, strSsrc, ResultCb, StreamStoppedCb](Uint32 ErrorCode, const CMkGbResponse* pGbRes) {
        const CMkGbInviteResponse* pRes = dynamic_cast<const CMkGbInviteResponse*>(pGbRes);
        MkRtpParam AnswerRtpParam;
        if (!pGbRes) {
            ResultCb(SystemError, "", AnswerRtpParam, nullptr);
            return;
        }
        MkString PlaySession = pRes->m_SessionId;
        if (NoneError != ErrorCode) {
            ResultCb(ErrorCode, "", AnswerRtpParam, nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        CMkSdp Sdp;
        ErrorCode = Sdp.ParseContent(pRes->m_AnwserSdp);
        if (NoneError != ErrorCode || Sdp.GetMedia().empty()) {
            ResultCb(SystemError, "", AnswerRtpParam, nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }

        MkString RemoteHost = Sdp.GetOrigin().GetAddr();
        if (!Sdp.GetConnection().GetAddr().empty()) {
            RemoteHost = Sdp.GetConnection().GetAddr();
        }
        CMkSdpMedia SdpMedia = *Sdp.GetMedia().begin();
        if (!SdpMedia.GetSdpConnection().GetAddr().empty()) {
            RemoteHost = SdpMedia.GetSdpConnection().GetAddr();
        }
        MkString RemoteSsrc;
        SdpMedia.GetExtendValue("y", RemoteSsrc);
        Uint32 Ssrc = CMkUtil::SafeAtoi(RemoteSsrc);
        if (0 == Ssrc) {
            Ssrc = std::stoi(strSsrc);
        }
        MkVector<CMkSdpAttribute>::const_iterator it = SdpMedia.GetAttributes().begin();
        for (; it != SdpMedia.GetAttributes().end(); it++) {
            if ("setup" == it->GetField()) {
                if ("passive" != it->GetValue()) {
                    ResultCb(SystemError, "", AnswerRtpParam, nullptr);
                    pRes->m_EventResCb(MkMapString(), "");
                    return;
                }
                break;
            }
        }

        if (SdpMedia.GetFmts().empty()
            || "PS" != SdpMedia.GetFmts().begin()->second.GetEncodingName()) {
            ResultCb(SystemError, "", MkRtpParam(), nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        AnswerRtpParam.bTcp = "TCP/RTP/AVP" == SdpMedia.GetProtocol();
        AnswerRtpParam.Host = RemoteHost;
        AnswerRtpParam.Port = SdpMedia.GetPort();
        AnswerRtpParam.Ssrc = Ssrc;
        AnswerRtpParam.Payload = SdpMedia.GetFmts().begin()->first;

        MkGbEndUserPlayParam Param;
        Param.PlaySession = pRes->m_SessionId;
        Param.ResultCb = ResultCb;
        Param.StoppedCb = StreamStoppedCb;
        m_MediaSourceLock.WLock();
        m_MapMediaSource[PlaySession] = Param;
        m_MediaSourceLock.WUnlock();
        ResultCb(NoneError, PlaySession, AnswerRtpParam, pRes->m_EventResCb);
    });
}

Uint32 CMkGbEndUser::StartPublish2(const MkString& SipUri, BOOL bTcpSip, const MkRtpParam& OfferRtpParam, const MkStreamReslutCallback& ResultCb, const MkStreamStoppedCallback& StreamStoppedCb)
{
    CMkSipUri Uri;
    Uint32 ErrorCode = Uri.Parse(SipUri);
    MkCheckErrorLog(ErrorCode, "input sipuri:%s invalid\n", SipUri.c_str());
    AddRouter(Uri.GetUserName(), "", Uri.GetHost(), Uri.GetPort(), bTcpSip);

    return InviteRequest(Uri.GetUserName(), Uri.GetUserName(), "", "", [this, ResultCb, StreamStoppedCb, OfferRtpParam](Uint32 ErrorCode, const CMkGbResponse* pGbRes) {
        const CMkGbInviteResponse* pRes = dynamic_cast<const CMkGbInviteResponse*>(pGbRes);
        MkRtpParam AnswerRtpParam;
        if (!pGbRes) {
            ResultCb(SystemError, "", AnswerRtpParam, nullptr);
            return;
        }
        MkString PlaySession = pRes->m_SessionId;
        if (NoneError != ErrorCode) {
            ResultCb(ErrorCode, "", AnswerRtpParam, nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        CMkSdp AnswerSdp;
        ErrorCode = AnswerSdp.ParseContent(pRes->m_AnwserSdp);
        if (NoneError != ErrorCode || AnswerSdp.GetMedia().empty()) {
            ResultCb(SystemError, "", AnswerRtpParam, nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }

        MkString RemoteHost = AnswerSdp.GetOrigin().GetAddr();
        if (!AnswerSdp.GetConnection().GetAddr().empty()) {
            RemoteHost = AnswerSdp.GetConnection().GetAddr();
        }
        CMkSdpMedia AnswerSdpMedia = *AnswerSdp.GetMedia().begin();
        if (!AnswerSdpMedia.GetSdpConnection().GetAddr().empty()) {
            RemoteHost = AnswerSdpMedia.GetSdpConnection().GetAddr();
        }
        MkString RemoteSsrc;
        AnswerSdpMedia.GetExtendValue("y", RemoteSsrc);
        Uint32 Ssrc = CMkUtil::SafeAtoi(RemoteSsrc);
        if (0 == Ssrc) {
            Ssrc = CMkUtil::GetRandomNumber();
        }
        MkVector<CMkSdpAttribute>::const_iterator it = AnswerSdpMedia.GetAttributes().begin();
        for (; it != AnswerSdpMedia.GetAttributes().end(); it++) {
            if ("setup" == it->GetField()) {
                if ("passive" != it->GetValue()) {
                    ResultCb(SystemError, "", AnswerRtpParam, nullptr);
                    pRes->m_EventResCb(MkMapString(), "");
                    return;
                }
                break;
            }
        }

        if (AnswerSdpMedia.GetFmts().empty()
            || "PS" != AnswerSdpMedia.GetFmts().begin()->second.GetEncodingName()) {
            ResultCb(SystemError, "", MkRtpParam(), nullptr);
            pRes->m_EventResCb(MkMapString(), "");
            return;
        }
        AnswerRtpParam.bTcp = "TCP/RTP/AVP" == AnswerSdpMedia.GetProtocol();
        AnswerRtpParam.Host = RemoteHost;
        AnswerRtpParam.Port = AnswerSdpMedia.GetPort();
        AnswerRtpParam.Ssrc = Ssrc;
        AnswerRtpParam.Payload = AnswerSdpMedia.GetFmts().begin()->first;

        MkGbEndUserPlayParam Param;
        Param.PlaySession = pRes->m_SessionId;
        Param.ResultCb = ResultCb;
        Param.StoppedCb = StreamStoppedCb;
        m_MediaSourceLock.WLock();
        m_MapMediaSource[PlaySession] = Param;
        m_MediaSourceLock.WUnlock();

        CMkSdp AckSdp;
        CMkSdpMedia AckSdpMedia;
        AckSdp.SetOrigin(CMkOrigin(GetSipId(), "0", "0", "IN", "IP4", OfferRtpParam.Host));
        AckSdp.SetConnection(CMkSdpConnection("IN", "IP4", OfferRtpParam.Host));
        AckSdpMedia.SetPort(OfferRtpParam.Port);
        AckSdpMedia.SetProtocol(OfferRtpParam.bTcp ? "TCP/RTP/AVP" : "RTP/AVP");

        AckSdp.SetVersion("0");
        AckSdp.SetSessionName("Play");
        AckSdpMedia.SetMediaType("video");
        CMkSdpFmt AckSdpFmt;
        AckSdpFmt.SetPayload(OfferRtpParam.Payload);
        AckSdpFmt.SetEncodingName("PS");
        AckSdpFmt.SetClockRate(MkDefaultClockRate);
        AckSdpMedia.AddFmt(AckSdpFmt);
        AckSdpMedia.AddAttr(CMkSdpAttribute("sendonly"));
        if (AnswerRtpParam.bTcp) {
            AckSdpMedia.AddAttr(CMkSdpAttribute("setup", "active"));   //mast active
            AckSdpMedia.AddAttr(CMkSdpAttribute("connection", "new"));
        }

        MkString StrAckSdp;
        AckSdpMedia.AddExtend("y", std::to_string(Ssrc));
        AckSdp.AddMedia(AckSdpMedia);
        AckSdp.ToString(StrAckSdp);
        pRes->m_EventResCb(MkMapString(), StrAckSdp);
        ResultCb(NoneError, PlaySession, AnswerRtpParam, pRes->m_EventResCb);
    });
}

Uint32 CMkGbEndUser::StartPublish(const MkString& ChannelId, const MkRtpParam& OfferRtpParam, const MkStreamReslutCallback& ResultCb, const MkStreamStoppedCallback& StreamStoppedCb)
{
    ResultCb(NoneError, "123", MkRtpParam(), nullptr);
    return NoneError;
}

Uint32 CMkGbEndUser::StopPublish(const MkString& SessionId)
{
    ByeRequest(SessionId, nullptr);
    DeletePlay(SessionId);
    return NoneError;
}

CMkGbEndUser::~CMkGbEndUser()
{

}